/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package controllers;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import listcontrol.LocalFileList;
import listcontrol.RemoteFileList;
import util.CompleteFile;
import util.IncompleteFile;
import util.RemoteFile;

/**
 *
 * @author Victor
 * Controlador das listas de arquivos
 */
public class DatabaseController {

    private final LocalFileList localList;
    private final HashMap<UUID, RemoteFileList> remoteList;
    public String SHARED_FOLDER_PATH = "./";

    public DatabaseController() {
        this.localList = new LocalFileList();
        this.remoteList = new HashMap<>();
    }

    // Faz as requisições de lista e atualiza o Banco
    void requestRemoteLists() {
        Collection<RemoteFileList> values = this.remoteList.values();
        Iterator<RemoteFileList> it = values.iterator();
        while (it.hasNext()) {
            RemoteFileList rList = it.next();
            if (!rList.isUpdated()) {
                rList.getFileList();
            }
        }
    }

    //      NECESSÁRIO APENAS POR ENQUANTO... APENAS A BUSCA SERÁ UTILIZADA
    void getAvailableFiles(ArrayList<RemoteFile> availableFiles) {
        this.requestRemoteLists();


        Collection<RemoteFileList> values = this.remoteList.values();
        Iterator<RemoteFileList> it = values.iterator();
        while (it.hasNext()) {
            RemoteFileList rList = it.next();
            for (int j = 0; j < rList.getNumberOfFiles(); j++) {
                CompleteFile file = rList.getFile(j);
                RemoteFile rFile = this.searchFile(availableFiles, file.getHashMD5());

                if (rFile == null) {
                    rFile = new RemoteFile();
                    rFile.setName(file.getName());
                    rFile.setHashMD5(file.getHashMD5());
                    rFile.setSize(file.getSize());
                    rFile.setComplete(file.isComplete());
                    if (file.isComplete()) {
                        rFile.setnSeeds(1);
                    } else {
                        rFile.setnLeechers(1);
                    }
                    availableFiles.add(rFile);
                } else {
                    if (file.isComplete()) {
                        rFile.setnSeeds(rFile.getnSeeds() + 1);
                    } else {
                        rFile.setnLeechers(rFile.getnLeechers() + 1);
                    }
                }
            }
        }

    }

    public RemoteFile searchFile(ArrayList<RemoteFile> availableFiles, byte hash[]) {
        boolean achou;
        for (int i = 0; i < availableFiles.size(); i++) {
            achou = true;
            for (int j = 0; j < 16; j++) {
                if (hash[j] != availableFiles.get(i).getHashMD5()[j]) {
                    achou = false;
                    break;
                }
            }
            if (achou) {
                return availableFiles.get(i);
            }
        }

        return null;
    }

    public ArrayList<RemoteFile> searchFiles(String filter) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public InetAddress getPeerId(UUID uuid) {
        return this.remoteList.get(uuid).getIp();
    }

    public boolean removePeer(InetAddress ip) {
        Iterator<RemoteFileList> list = this.remoteList.values().iterator();

        while (list.hasNext()) {
            if (list.next().getIp().equals(ip)) {
                list.remove();
                return true;
            }
        }
        return false;
    }

    public boolean addCompleteFile(CompleteFile completeFile) {
        return this.localList.addCompleteFile(completeFile);
    }

    public boolean removeCompleteFile(String completeFile) {
        return this.localList.removeCompleteFile(completeFile);
    }

    public Long getLocalVersion() {
        return this.localList.getVersion();
    }

    public void incLocalVersion() {
        this.localList.incVersion();
    }

    public String getLocalUUIDString() {
        return this.localList.getUUIDString();
    }

    public CompleteFile searchLocalFile(byte[] hash) {
        return localList.retrieveFile(hash);
    }

    public void rebuildLocalList() {
        this.localList.rebuildList();
    }

    public RemoteFileList searchPeer(UUID uuid) {
        return this.remoteList.get(uuid);
    }

    public boolean addPeer(RemoteFileList peer) {
        if (this.remoteList.put(peer.getUUID(), peer) == null) {
            return false;
        } else {
            return true;
        }
    }

    public int getNumberOfLocalFiles() {
        return this.localList.getNumberOfFiles();
    }

    public Collection<CompleteFile> getLocalFileCollection() {
        return this.localList.getFileCollection();
    }

    /**
     * Choose a peer that has the file indicated by the hash and can hold new connections 
     * acording to IncompleteFile.MAX_CONNECTIONS_PER_PEER constant
     * @param hash Hash MD5 of the file
     * @return The peer if someone meet the pre-conditions or null
     */
    public RemoteFileList choosePeer(byte[] hash) {
        Iterator it = this.remoteList.entrySet().iterator();
        RemoteFileList leecher = null;
        while (it.hasNext()) {
            Map.Entry pair = (Map.Entry) it.next();
            RemoteFileList list = (RemoteFileList) pair.getValue();
            if (list.getConnections() < IncompleteFile.MAX_CONNECTIONS_PER_PEER) {
                CompleteFile file = list.retrieveFile(hash);
                if (file != null) {
                    if (file.isComplete()) {
                        list.setConnections(list.getConnections() + 1);
                        return list;
                    } else {
                        leecher = list;
                    }
                }
            }
        }

        if (leecher != null) {
            leecher.setConnections(leecher.getConnections() + 1);
            return leecher;
        }

        return null;
    }

    public HashMap<UUID, RemoteFileList> getRemoteListLock() {
        return this.remoteList;
    }
}
